#!/usr/bin/ruby
require 'cgi'

# Set this to the path of your database file.
# $FILE = "/cs/cgi/people/jdahern/database.txt"
$FILE = "/Users/jdahern/Documents/Dropbox/CS372/ass8/database.txt"
$cgi =  CGI.new("html3")

###############################################################
# User
###############################################################
class User
  attr_reader :username, :name, :friends, :sex, :password
  attr_writer :username, :name, :sex, :password
  
  def initialize(username,name,friends,sex,password)
   @username = username
   @name = name
   @friends = friends
   @sex = sex
   @password = password
   # print to_out
  end

  def to_out
     s = ""
     @friends.each do |key, value|
           s = s + key + "/" + value + " "
         end
     n = "USERNAME\t#{@username}\nNAME\t\t#{@name}\nSEX\t\t#{@sex}\nPASSWORD\t#{@password}\nFRIENDS\t\t" + s + "\nEND\n\n"
     return n
  end
end
###############################################################
# Database
###############################################################
class Database
  @@db = {}
  def initialize()
    load()
  end
  
  def [] (name)
    out = ""
    users {|x| x[1].name = name}
  end
  
  def []= (name,user)
    users do |x|
      if x[1].username == user then
        x[1].name = ""
        x[1].name = name
        save()
      end
    end
  end

  def users()
     @@db.find do |x|
       yield x
     end 
  end
  
  def exists_user?(name)
    out = ""
    users do |x|
      out = x[0]
      if (out) == name then
        return true
      end
    end
    return false
  end
  
  def add(username, userObj)
    @@db[username] = userObj
    save()
  end
    
  
	def exists_userpswd?(username, pswd)
	out = ""
		users do |x|
			out = x[0]
			if exists_user?(username) then
				if(x[1].password == pswd) then
					return true						
				end
			else
				return false		
			end	
		end
	end

  def read_line (database)
    File.open(database) do |file|
      file.each do |line| 
        if line =~ /[\t]/ then
          yield line
        elsif line =~ /[#]/ then
            # Do Nothing
        elsif line =~ /[a-z]+|\t+/ then
          yield line
        else
            # Do Nothing
          end
      end 
    end
  end
  
  def split_line (line)
    a = []
    line.scan(/[a-z]+[ ]*[a-z]+/) do |word|
      a << word
    end
    return a
  end
  
  def load()
    username = name = password = sex = nil
    friends = {}
    data = ""
    f = File.open($FILE, "r")
    f.each_line do |line|
      friends = {}
      args = line.scan(/[^\s]+/)
        case args[0]
          when "USERNAME" then username = args[1]
          when "NAME" then output = ""; for x in 1...args.length; output += "#{args[x]} "; end; name = output
          when "SEX" then sex = args[1]
          when "PASSWORD" then password = args[1]
          when "FRIENDS" then 
            j = 1
            while j < (args.length)
              for i in args[j].scan(/[a-zA-Z0-9\/]+/)
                a = i.scan(/[a-zA-Z0-9]+/)
                friends[a[0]] = a[1]
              end
              j += 1
            end
          @@db[username] = User.new(username,name,friends,sex,password)
        end
      end
      f.close
    end
  
  def save()
    outputstr = ""
    File.open($FILE, "w") do |file|
      users do |x|
        outputstr = ""
        outputstr = x[1].to_out
        file.puts "#{outputstr}"
      end
    end
  end
end

###############################################################
#                         Facebook
###############################################################
class Facebook

# The database itself.
@@db = nil

# body is the HTML that you want to transfer to the user.
def Facebook.writeHTML(body)
   $cgi.out { 
     $cgi.html { 
       $cgi.body {
         "\n\n"+body+"\n"
       }
     }
   }
end


################################################################
# Welcome screen
################################################################

def Facebook.welcomeForm()
   return <<-END
   <center><h1>Welcome to 372book</h1></center><br>
   <form target="web.cgi">
      <center> 
         <input type="submit" name="action" value="login">
         <input type="submit" name="action" value="create account">
         <input type="hidden" name="username" value="">
      </center>
   </form>
END
end

def Facebook.welcomeScreen()
   writeHTML(welcomeForm())
end

###############################################################
#                    Login Screen
###############################################################

# This method returns two values, the first one is true
# if login was successful, false otherwise. The second 
# argument is an error message useful when the login was
# unsuccessful. (Yes, using exceptions might be better,
# but I hate exceptions.)
# Error messages returned: 
#    "cannot log in #{username}: no such user"
#    "cannot log in #{username}: wrong password"
def Facebook.login(username,password)
	
	a = Database.new	
	if(a.exists_user?(username)) then		
		if(a.exists_userpswd?(username, password)) then
			return true, ""
    else
      return false, "cannot log in #{username}: wrong password"
		end	
  else
    return false,"cannot log in #{username}: no such user" 
	end
end

# Return the HTML for the login screen. "message"
# is an error message (in case the user has made
# an unsuccessful login attempt). Leave it blank
# the first time.
def Facebook.loginForm(message)
   return <<-END
   <center>
       <h1>Log in to 372book</h1><br>
       <font color="red">#{message}</font><br>
       <form target="web.cgi">
          username: <input type="text" name="username"><br>
          password: <input type="password" name="password"><br>
          <input type="submit" name="action" value="login">
          <input type="hidden" name="username" value="">
   </form>
   </center>
END
end

def Facebook.loginScreen()
   username =  $cgi["username"]
   password =  $cgi["password"]
   if username != "" then
      Facebook.load()
      ok, message = Facebook.login(username,password)
      if ok then
         Facebook.profileScreen(username)
      else
         writeHTML(loginForm(message))
      end
   else 
      writeHTML(loginForm(""))
   end
end

################################################################
#                        Logout form
################################################################

def Facebook.logoutScreen()
   writeHTML(welcomeForm())
end

################################################################
#                 Create new user screen
################################################################

# A username consists of a letter (upper or lower case)
# followed by at least one or more letters or digits.
def Facebook.okUsername(un)
	if un =~ /[a-z]+[0-9]*/ then true else false end
end

# A password consists of four or moour letters or digits.
def Facebook.okPassword(pw)
	if pw =~ /[a-z0-9][a-z0-9][a-z0-9][a-z0-9]+/ then true else false end
end

# Sex is one of Male or Female.
def Facebook.okSex(sex)
   if sex =~ /Male|Female/ then true else false end
end

# user is an object of type User, containing all the 
# information gathered about the new user. We return
# two values, a boolean which is true if the
# user has been successfully added to the Database
# and false otherwise. On a failed attempt, one of
# these error messages is generated:
#    "cannot create #{username}: user already exists"
#    "cannot create #{username}: illegal username"
#    "cannot create #{username}: illegal password"
#    "cannot create #{username}: choose Male or Female"
def Facebook.createUser(user)
   a = Database.new
   username = user.username()
   password = user.password()
   sex = user.sex()
   if(a.exists_user?(username)) then
    return false, "cannot create #{username}: user already exists"
  elsif(!Facebook.okUsername(username)) then
      return false, "cannot create #{username}: illegal username"
    elsif(!Facebook.okPassword(password)) then
      return false, "cannot create #{username}: illegal password"
    elsif(!Facebook.okSex(sex)) then
      return false, "cannot create #{username}: choose Male or Female"
    else
      return true, ""
  end
end


# Return the HTML for the create user screen. "message"
# is an error message. Leave it blank the first time.
def Facebook.createForm(message)
   return <<-END
   <center>
       <h1>Create new account</h1><br>
       <font color="red">#{message}</font><br>
       <form target="web.cgi">
          username: <input type="text" name="username"> <br>
          name: <input type="text" name="realname"> <br>
          password: <input type="password" name="password"> <br>
          sex: <input type="radio" name="sex" value="Male"> Male
               <input type="radio" name="sex" value="Female"> Female <br>
         <input type="submit" name="action" value="save account">
         <input type="hidden" name="username" value="">
   </form>
END
end

# This is where we arrive the first time the user hits the
# create account button:
def Facebook.createScreen()
   writeHTML(createForm(""))
end

# After the user has filled in data into the account screen
# and hits "create account", we'll arrive here. We check
# for valid data and either go to the profile screen (if
# all was OK) or back to the create account screen if there
# was an error.
def Facebook.saveCreatedUser()
   username =  $cgi["username"]
   password =  $cgi["password"]
   realname =  $cgi["realname"]
   sex      =  $cgi["sex"]
   if username != "" then
       Facebook.load()
       person = User.new(username, realname, {}, sex, password)
       ok, message = Facebook.createUser(person)
       if ok then
         a = Database.new
         a.add(username, person)
        Facebook.profileScreen(username)
       else
          writeHTML(createForm(message))
       end
    else 
       writeHTML(createForm(""))
    end
end

################################################################
# User profile page
################################################################

# Do a breadth first search of the friend graph. queue holds
# a list of usernames which we have yet to process. visited
# is a hashtable from usernames to the lever where they were
# first found. The routine returns 'counts', an array where
# count[0] is the number of users at that level from the
# current user (always 0), count[1] is the user's number of
# direct friends, count[2] is the number of friends 2 steps
# away, etc.
def Facebook.BFS(queue, visited, counts, currentlevel)
   # YOUR CODE HERE
   return counts
end

# This routine returns 'counts', an array where
# count[0] is the number of users at that level from the
# current user (always 0), count[1] is the user's number of
# direct friends, count[2] is the number of friends 2 steps
# away, etc.
def Facebook.levels(username)
   counts = [1]
   visited = {username=>0}
   queue = [username]
   # YOUR CODE HERE
end

# On the profile page we list all the user's friends,
# along with a button they can press to go to that
# person's profile page. This routine returns the
# corresponding HTML. 'friend' is the user name of
# the friend, 'how' is how we know that friend.
def Facebook.viewFriendEntry(friend,how)
   return "YOUR CODE HERE"
end

# Return the HTML for a user.
def Facebook.profileForm(username)
   counts = Facebook.levels(username)
   form = <<-END
   <center><h1>Profile for #{username}</h1></center><br> 
   <form target="web.cgi">
   YOUR CODE HERE
   <center>
      <input type="submit" name="action" value="home"> 
      <input type="submit" name="action" value="edit"> 
      <input type="submit" name="action" value="logout">
      <input type="hidden" name="username" value="#{$username}">
  </center>
   </form>
END
end

def Facebook.profileScreen(username)
   Facebook.load()
   writeHTML(profileForm(username))
end

################################################################
#                        View friend form
################################################################


def Facebook.viewFriendScreen(friend)
   Facebook.load()
   writeHTML(profileForm(friend))
end

################################################################
#                      Edit page
################################################################

# Return the HTML for the user's edit page.
def Facebook.editForm(username)
   form = <<-END
   <center><h1>Edit your profile information </h1></center><br>
   <form target="web.cgi">
   YOUR CODE HERE
   <br>
   <center>
      <input type="submit" name="action" value="save edits">
      <input type="submit" name="action" value="home">
      <input type="submit" name="action" value="logout">
      <input type="hidden" name="username" value="#{$username}">
   </center>
END
end

# When the user first hits the EDIT-button, we land here.
def Facebook.editScreen()
   Facebook.load()
   writeHTML(editForm($username))
end

# When he hits the SAVE EDITS-button, we land here.
# Save the edits in the database, write the database
# file back out again, and return the user's profile
# page.
def Facebook.saveEdits()
   Facebook.load()
   name = $cgi['name']
   password = $cgi['password']
   sex = $cgi['sex']
   friends = {}
   # YOUR CODE HERE
end

################################################################
#                  Database load and store
################################################################

def Facebook.load()
  @@db = Database.new
  @@db.load()
  return true, "database loaded"
end

def Facebook.save()
  @@db.save()
  return true, "database saved"
end

end


################################################################
# Main section
################################################################


action = $cgi["action"]
$username = $cgi['username']
#STDERR.puts "ACTION="+action + " username=" + $username
case action
   when ""               then Facebook.welcomeScreen()
   when "login"          then Facebook.loginScreen()
   when "logout"         then Facebook.logoutScreen()
   when "create account" then Facebook.createScreen()
   when "save account"   then Facebook.saveCreatedUser()
   when "edit"           then Facebook.editScreen()
   when "save edits"     then Facebook.saveEdits()
   when "home"           then Facebook.profileScreen($username)
   when /^view [a-zA-Z0-9]+$/         then Facebook.viewFriendScreen($cgi["action"].scan(/[a-zA-Z0-9]+$/)[0])
end
